# Copyright (c) 2008 ToI-Planning, All rights reserved.
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# $Id: App.pm 11 2009-06-23 09:41:59Z usualoma $

package Dumper::App;
use strict;
use File::Basename;
use File::Spec;
use File::Path;
use MT::Util qw( start_end_day start_end_week 
                 start_end_month week2ymd archive_file_for
                 format_ts offset_time_list first_n_words dirify get_entry
                 encode_html encode_js remove_html wday_from_ts days_in
                 spam_protect encode_php encode_url decode_html encode_xml
                 decode_xml relative_date asset_cleanup
                 ts2epoch iso2ts );

sub fix_path {
	my ($site_path, $file) = @_;
	if ($file !~ m/^\//) {
		File::Spec->catfile($site_path, $file);
	}
	else {
		$file;
	}
}

sub read_data {
	my ($obj, $blog, $hash) = @_;
	if (! $blog) {
		my $app = MT->instance;
		my $blog_id = '';
		if (
			$app->can('param')
			&& ($blog_id = $app->param('blog_id'))
		) {
			$blog = MT::Blog->load($blog_id);
		}
		else {
			return {};
		}
	}
	elsif (! ref $blog) {
		$blog = MT::Blog->load($blog);
	}
	if (! $hash) {
		my $plugin = MT->component('Dumper');
		$hash = $plugin->get_config_hash('blog:' . $blog->id);
	}

	my $site_path = $blog->site_path;
	my $ds = (ref $obj) ? $obj->datasource : $obj;
	my ($fname, $fpath);

	if (
		$hash->{'dump_' . $ds . '_yaml'}
		&& ($fname = $hash->{'dump_' . $ds . '_yaml_file'})
		&& ($fpath = &fix_path($site_path, $fname))
		&& (-f $fpath)
	) {
		require YAML::Tiny;
		my $yaml = eval { YAML::Tiny->read($fpath) };
		if (ref($yaml)) {
			return $yaml->[0];
		}
	}

	if (
		$hash->{'dump_' . $ds . '_json'}
		&& ($fname = $hash->{'dump_' . $ds . '_json_file'})
		&& ($fpath = &fix_path($site_path, $fname))
		&& (-f $fpath)
	) {
		require JSON;
		my $conv = new JSON;
		my $obj = $conv->jsonToObj(do {
			open(my $fh, '<', $fpath); local $/; <$fh>
		});
		if (ref $obj) {
			return $obj;
		}
	}

	return {};
}

sub write_data {
	my ($obj, $blog, $hash, $data) = @_;
	my $ds = (ref $obj) ? $obj->datasource : $obj;
	my $site_path = $blog->site_path;
	my ($fname, $fpath);

	if (
		$hash->{'dump_' . $ds . '_yaml'}
		&& ($fname = $hash->{'dump_' . $ds . '_yaml_file'})
	) {
		$fpath = &fix_path($site_path, $fname);
		if (! -f $fpath) {
			mkpath(dirname($fpath));
		}

		require YAML::Tiny;
		my $yaml = YAML::Tiny->new;
		$yaml->[0] = $data;
		$yaml->write($fpath);
	}

	if (
		$hash->{'dump_' . $ds . '_json'}
		&& ($fname = $hash->{'dump_' . $ds . '_json_file'})
	) {
		$fpath = &fix_path($site_path, $fname);
		if (! -f $fpath) {
			mkpath(dirname($fpath));
		}

		require JSON;
		my $conv = new JSON;
		my $json = $conv->objToJson($data);
		open(my $fh, '>', $fpath);
		print($fh $json);
		close($fh);
	}
}

sub get_obj_data {
	my ($blog_id, $cfg, $ds, $obj) = @_;

	my $authored_on = $obj->authored_on;
	if ($authored_on =~ m/\s/) {
		$authored_on =~ s/\D//g;
		$obj->authored_on($authored_on);
	}

	my $hash = {};

	my %fields = %{ $cfg->{'dump_' . $ds . '_fields'} || {} };
	my $props = $obj->properties;

	my @keys = (
		keys(%{ $props->{column_defs} }),
		'permalink',
	);
	foreach my $c (@keys) {
		my $k = $c;
		my $value = $obj->$c;

		if (%fields) {
			if (! $fields{$c}) {
				next;
			}
			else {
				$k = $fields{$c};
			}
		}
		elsif ($c eq 'meta') {
			next;
		}

		if ($c =~ m/_on$/ && $value =~ m/\s/) {
			$value =~ s/\D//g;
			$value = &ts2epoch($value);
		}
		$hash->{$k} = $value;
	}

	eval { require CustomFields::Field; };
	if (! $@) {
		require CustomFields::Util;
		my $meta = CustomFields::Util::get_meta($obj);
		my $terms = {
			'blog_id' => [0, $blog_id],
			'obj_type' => $ds,
		};
		if (%fields) {
			$terms->{'tag'} = [ keys(%fields) ];
		}
		my @fields = CustomFields::Field->load($terms);
		foreach my $f (@fields) {
			my $k = $f->tag;
			if (%fields) {
				$k = $fields{$k} || $k;
			}
			$hash->{$k} = $meta->{$f->basename};
		}
	}

	$obj->authored_on($authored_on);

	$hash;
}

sub on_save {
	my ($cb, $obj, $original) = @_;
	my $app = MT->instance;
	my $plugin = MT->component('Dumper');
	my $blog = $obj->blog;
	my $blog_id = $blog->id;
	my $ds = $obj->datasource;
	my $cfg = $plugin->get_config_hash('blog:' . $blog_id) || {};

	if (! $cfg->{'dump_' . $ds}) {
		return;
	}

	my $data = &read_data($obj, $blog, $cfg);
	my $entry_id = $obj->id;

	if ($cb->method =~ m/save/ && $obj->status == MT::Entry::RELEASE()) {
		$data->{$entry_id} = &get_obj_data($blog_id, $cfg, $ds, $obj);
	}
	else {
		delete($data->{$entry_id}) if $data->{$entry_id};
	}

	&write_data($obj, $blog, $cfg, $data);
}

sub plugin_data_post_save {
	my ($cb, $obj, $original) = @_;
	my $plugin = MT->component('Dumper');
	my $app = MT->instance;

	if (lc($app->param('plugin_sig')) ne $plugin->id) {
		return;
	}

	my $blog_id = $app->param('blog_id')
		or return;

	require MT::Blog;
	require MT::Entry;

	my $blog = MT::Blog->load($blog_id);

	my $cfg = $plugin->get_config_hash('blog:' . $blog_id);

	foreach my $ds ('entry', 'page') {
		if (
			(
				$app->param('dump_' . $ds . '_yaml')
				&& $app->param('dump_' . $ds . '_yaml_file')
				&& $app->param('dump_' . $ds . '_yaml_file_rebuild')
			)
			||
			(
				$app->param('dump_' . $ds . '_json')
				&& $app->param('dump_' . $ds . '_json_file')
				&& $app->param('dump_' . $ds . '_json_file_rebuild')
			)
		) {
			my $data = {};
			my $class = $app->model($ds);
			my $iter = $class->load_iter({
				'blog_id' => $blog_id,
				'status' => MT::Entry::RELEASE()
			});
			
			while (my $obj = $iter->()) {
				$data->{$obj->id} = &get_obj_data($blog_id, $cfg, $ds, $obj);
			}

			&write_data($ds, $blog, $cfg, $data);
		}
	}

}

1;
